/**
 *Copyright (c) 2024 watereyes
 * workflow is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package com.koron.workflow.api.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import org.activiti.bpmn.model.ExtensionAttribute;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;
import org.swan.bean.MessageBean;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.base.Objects;
import com.koron.util.Tools;
import com.koron.workflow.api.bean.CommunicatingBean;
import com.koron.workflow.api.bean.HistoryBillsBean;
import com.koron.workflow.api.bean.HistoryTaskMsgBean;
import com.koron.workflow.api.bean.RelationBean;
import com.koron.workflow.api.bean.SubHistoryBillsBean;
import com.koron.workflow.api.bean.TimerBean;
import com.koron.workflow.api.bean.query.ProcessCommitQueryBean;
import com.koron.workflow.api.bean.query.ProcessListQueryBean;
import com.koron.workflow.api.bean.query.ProcessStartQueryBean;
import com.koron.workflow.api.bean.query.RelationQueryBean;
import com.koron.workflow.api.dto.HistoryTaskMsgDto;
import com.koron.workflow.api.dto.NextTaskNodeInfoDto;
import com.koron.workflow.api.dto.NodeInfoDto;
import com.koron.workflow.api.dto.TaskInfoDto;
import com.koron.workflow.api.dto.TaskListDto;
import com.koron.workflow.api.dto.TaskNodeInfoDto;
import com.koron.workflow.api.mapper.CommunicatingMapper;
import com.koron.workflow.api.mapper.HistoryBillsMapper;
import com.koron.workflow.api.mapper.HistoryTaskMsgMapper;
import com.koron.workflow.api.mapper.SubHistoryBillsMapper;
import com.koron.workflow.api.mapper.TaskListMapper;
import com.koron.workflow.api.mapper.TimerMapper;
import com.koron.workflow.api.service.ActivitiService;
import com.koron.workflow.api.service.HistoryTaskMsgService;
import com.koron.workflow.api.service.RelationService;
import com.koron.workflow.api.service.TaskListService;
import com.koron.workflow.api.service.WorkflowService;
import com.koron.workflow.api.util.ApiUtils;
import com.koron.workflow.applicationManag.bean.AppBean;
import com.koron.workflow.common.bean.MessageEntity;
import com.koron.workflow.common.bean.StaffBean;
import com.koron.workflow.common.util.BusinessCode;
import com.koron.workflow.common.util.DateUtils;
import com.koron.workflow.common.util.SessionFactoryLocal;
import com.koron.workflow.common.util.WorkflowUtil;
import com.koron.workflow.workflowDesign.bean.ProcessBean;
import com.koron.workflow.workflowDesign.mapper.ProcessMapper;

/**
 * 流程平台对外接口业务处理类
 * @author 周利斌
 * @date 2020-04-12
 */
@Service
public class WorkflowServiceImpl implements WorkflowService {
	
	private static Logger log = LoggerFactory.getLogger(WorkflowServiceImpl.class);

	@Autowired
	private ActivitiService activitiService;
	
	@Autowired
	private RelationService relationService;
	
	@Autowired
	private TaskListService taskListService;
	
	@Autowired
	private HistoryTaskMsgService historyTaskMsgService;
	
	/**
	 * 流程启动业务处理实现
	 * @param factory
	 * @param param 
	 * @return 
	 */
	@Override
	@TaskAnnotation("start")
	public MessageBean<?> start(SessionFactory factory, ProcessStartQueryBean param) {
		try{
			SessionFactoryLocal.set(factory);
			HistoryBillsMapper historyBillsMapper = factory.getMapper(HistoryBillsMapper.class);
			HistoryBillsBean historyBills = historyBillsMapper.findByBillId(param.getBillId());
			if (historyBills != null) {
				return MessageBean.create(BusinessCode.FAILURE.getCode(), "存在重复的单据 billId", Void.class);
			}
			Map<String, Object> vars = param.getFormVariables();
			ProcessDefinition pd = activitiService.getLastProcessDefinition(param.getSetCode() + "_" + param.getProcessCode());
			if(pd == null) {
				return MessageBean.create(BusinessCode.FAILURE.getCode(), "该流程模板未发布", Void.class);
			}
			UserTask userTask = activitiService.getDraftNode(pd.getId());
			if(userTask != null && userTask.getCandidateUsers().size() > 0) {
				List<String> draftNodeUsers = userTask.getCandidateUsers();
				Map<String, List<ExtensionAttribute>> operationmap = userTask.getExtensionElements().get("operation").get(0).getAttributes();
				String filterRule = operationmap.get("filter_rule").get(0).getValue(); //获取过滤规则
				String autoPickRule = operationmap.get("auto_pick_rule").get(0).getValue(); //获取自动选人规则
				draftNodeUsers = ApiUtils.convertUsers(draftNodeUsers, param.getOperator(), filterRule, autoPickRule, vars);
				if(!draftNodeUsers.contains(param.getOperator())) {
					return MessageBean.create(BusinessCode.FAILURE.getCode(), "当前人员未在起草节点候选人中，禁止开启流程实例!", Void.class);
				}
			}
			ProcessInstance processInstance = activitiService.startProcessInstanceByKey(param.getSetCode() + "_" + param.getProcessCode(), param.getOperator(), param.getTitle(), param.getBillId(), vars);
			List<Task> tasks = activitiService.getTaskByProcessInsId(processInstance.getId());
	        if (null == tasks || tasks.size() == 0) {
	            return MessageBean.create(BusinessCode.FAILURE.getCode(), "实例启动失败，获取当前任务失败!", Void.class);
	        }
	        // 添加到流程单据信息
	        AppBean app = WorkflowUtil.getApp(param.getAppCode());
	        HistoryBillsBean historyBill = new HistoryBillsBean(param.getBillId(), app.getId(), param.getProcessId(), processInstance.getId(), param.getTitle(), param.getBillNo());
	        if(vars.containsKey("urgent")) {
	        	historyBill.setUrgent((int) vars.get("urgent"));
	        }
	        StaffBean staff = WorkflowUtil.getStaffByAccount(param.getOperator());
	        if(staff == null) {
	        	return MessageBean.create(BusinessCode.FAILURE.getCode(), "当前操作人账号不存在", Void.class);
	        }
	        historyBill.setCreateInfo(staff);
	        factory.getMapper(HistoryBillsMapper.class).addHistoryBills(historyBill);
	        if (StringUtils.isNotEmpty(param.getOtherNodeCandidates())) {
            	//更新实例变量中  节点候选组信息
               activitiService.saveOtherNodeCandidates(param.getOtherNodeCandidates(), processInstance.getId());
            }
	        // 假如流程启动后 节点为起草节点则自动办理起草节点
	        if(tasks.size() == 1 && activitiService.isDraftNode(tasks.get(0))) {
	            Task task = tasks.get(0);        	
	            // 起草节点自动办理
	            activitiService.complete(task.getId(), param.getOperator(), param.getTitle(), vars);
	            tasks = activitiService.getTaskByProcessInsId(processInstance.getId());
	            historyTaskMsgService.addHistoryTaskMsg(factory, param.getOperator(), task.getId(), ApiUtils.transOperation(32), param.getComment(), param.getAttachment()); 
	        }
	        List<NextTaskNodeInfoDto> list = new ArrayList<NextTaskNodeInfoDto>();
	        // 子流程处理       
	        for(Task t : tasks) {
	        	String subFlowCode = activitiService.getSubTaskCode(t);
	        	// 假如该任务节点为子流程节点
	        	if(StringUtils.isNotBlank(subFlowCode)) {
	        		list.add(startSubProcess(factory, param, t, subFlowCode));
	        	} else {
	        		TimerMapper timerMapper = factory.getMapper(TimerMapper.class);
	        		TimerBean timer = timerMapper.findByTaskIdAndStatus(t.getId(), 0);
	        		if(timer != null) {
	        			activitiService.complete(t.getId(), "system_operator", param.getTitle(), vars);
	        			timerMapper.updateStatus(t.getId(), 1);
	        			tasks = activitiService.getTaskByProcessInsId(processInstance.getId());
	        		}
	        	} 
	        }
	        // 获取下一个任务节点的信息
	        list.add(activitiService.getNextTaskNodeInfoDto(tasks));
	        NextTaskNodeInfoDto dto = new NextTaskNodeInfoDto();
	        dto.setProcessInstanceId(processInstance.getId());
	        List<NodeInfoDto> curNodes = new ArrayList<NodeInfoDto>();
	        List<TaskInfoDto> ts = new ArrayList<TaskInfoDto>();
	        for(NextTaskNodeInfoDto taskNode : list) {
	        	curNodes.addAll(taskNode.getCurNodes());
	        	ts.addAll(taskNode.getTasks());
	        }
	        dto.setCurNodes(curNodes);
	        dto.setTasks(ts);
	        dto.setCandidatesName(ts.stream().map(t -> t.getUserName()).collect(Collectors.joining(",")));
	        MessageBean<NextTaskNodeInfoDto> message = MessageBean.create(BusinessCode.SUCCESS.getCode(), "流程启动成功", NextTaskNodeInfoDto.class);
	        message.setData(dto);
	        log.debug("流程启动成功：{}", processInstance.getId());
	        return message;
		}finally{
			SessionFactoryLocal.remove();
		}
	}

	@Override
	@TaskAnnotation("submit")
	public MessageBean<?> submit(SessionFactory factory, ProcessCommitQueryBean param) {
		try {
			SessionFactoryLocal.set(factory);
			Task task = activitiService.getTaskById(param.getTaskId());
	        if (null == task) {
	            return MessageBean.create(BusinessCode.FAILURE.getCode(), "当前任务不存在!", Void.class);
	        }
	        HistoryBillsBean bill = factory.getMapper(HistoryBillsMapper.class).findByBillId(param.getBillId());
	        if (bill == null) {
	            return MessageBean.create(BusinessCode.FAILURE.getCode(), "单据信息未找到", Void.class);
	        }
	        List<String> candidateUsers = activitiService.getCandidateUsers(task.getId());
	        if(param.getOperation() != OPERATION_REPLY && param.getOperation() != OPERATION_ALRAM && param.getOperation() != OPERATION_RECALL && param.getOperation() != OPERATION_SUSPEND && !candidateUsers.contains(param.getOperator())) {
	        	return MessageBean.create(BusinessCode.FAILURE.getCode(), "当前人员未在候选列表中，禁止继续操作!", Void.class);
	        }
	        if(StringUtils.isNotEmpty(task.getAssignee()) && !task.getAssignee().equals(param.getOperator())) {
	        	return MessageBean.create(BusinessCode.FAILURE.getCode(), "当前处理人与该任务指定办理人不符，禁止操作!", Void.class);
	        }
	        Map<String, Object> variables = param.getFormVariables();
	        String operatopn = ApiUtils.transOperation(param.getOperation());
	        String processInstanceId = task.getProcessInstanceId();
	        List<Task> tasks = new ArrayList<Task>();
	        List<NextTaskNodeInfoDto> list = new ArrayList<NextTaskNodeInfoDto>();
	        switch (param.getOperation()) {
				case OPERATION_REJECT:{  //驳回
					if (StringUtils.isEmpty(param.getRejectNodeCode())) {
	                    return MessageBean.create(BusinessCode.FAILURE.getCode(), "驳回时请设置要驳回的节点名", Void.class);
	                }
					if(param.getRejectNodeCode().startsWith("1-")) {
						activitiService.suspendProcessInstance(task.getProcessInstanceId());
						param.setRejectNodeCode(param.getRejectNodeCode().substring(2));
						SubHistoryBillsMapper subMapper = factory.getMapper(SubHistoryBillsMapper.class);
						SubHistoryBillsBean subBill = subMapper.findByProcessInstanceId(task.getProcessInstanceId());
						task = activitiService.getTaskById(subBill.getTaskId());
						processInstanceId = task.getProcessInstanceId();
					}
					activitiService.backProcess(task, bill.getTitle(), param.getOperator(), param.getRejectNodeCode(), variables);
					break;
				}
				case OPERATION_TRANSFER:{ //转办
					if (StringUtils.isEmpty(param.getRelatedUser())) {
	                    return MessageBean.create(BusinessCode.FAILURE.getCode(), "转办操作必须设定转办人", Void.class);
	                }
					StaffBean staff = WorkflowUtil.getStaffByAccount(param.getRelatedUser());
					if(staff == null) {
						return MessageBean.create(BusinessCode.FAILURE.getCode(), "转办操作请设置符合要求的转办人", Void.class);
					}
					if(StringUtils.isNotBlank(task.getAssignee())) {
						activitiService.transferAssignee(task.getId(), staff.getAccount());
					} else {
						activitiService.deleteCandidateUser(task.getId(), param.getOperator());
						activitiService.addCandidateUser(task.getId(), staff.getAccount());
					}
					operatopn = operatopn + "(" + staff.getName() + ")";
					//修改关系状态为已转办
					relationService.updateRelation(factory, 2, new RelationBean(RelationBean.R_HANDLE, param.getOperator(), task.getProcessInstanceId(), task.getId(), RelationBean.STATUS_UNHANDLE), null);
					relationService.addRelation(factory, 2, new RelationBean(RelationBean.R_HANDLE, param.getOperator(), task.getProcessInstanceId(), task.getId(), RelationBean.STATUS_UNHANDLE), staff.getAccount());
					taskListService.updateTaskListOperator(factory, task.getId(), staff.getAccount());
					break;
				}
				case OPERATION_COMMUNICATE:{ //添加沟通人
					 if (StringUtils.isEmpty(param.getRelatedUser())) {
	                     return MessageBean.create(BusinessCode.FAILURE.getCode(), "设置沟通人操作必须设定相关人员", Void.class);
	                 }
					 String relatedUser = param.getRelatedUser();
					 String[] array = relatedUser.split(",");
					 List<String> staffs = new ArrayList<String>();
					 for(String user : array) {
						 StaffBean staff = WorkflowUtil.getStaffByAccount(user);
						 if(staff == null) {
							 continue;
						 }
						 staffs.add(staff.getName());
						 CommunicatingBean bean = new CommunicatingBean();
						 bean.setTaskId(task.getId());
						 bean.setFrom(param.getOperator());
						 bean.setTo(staff.getAccount());
						 bean.setId(WorkflowUtil.getObjectId());
						 factory.getMapper(CommunicatingMapper.class).addCommunicating(bean);
					 }
					 operatopn = operatopn + "(" + StringUtils.join(staffs, ",") + ")";
					 //添加待回复的关系记录
					 relationService.addRelation(factory, 4, new RelationBean(RelationBean.R_REPLY, param.getOperator(), task.getProcessInstanceId(), task.getId(), RelationBean.STATUS_UNHANDLE), param.getRelatedUser());
					 break;
				}
				case OPERATION_ALRAM:{    //催办
					if (StringUtils.isEmpty(param.getComment())) {
	                    return MessageBean.create(BusinessCode.FAILURE.getCode(), "请传入催办的内容", Void.class);
	                }
					alram(task, param.getComment(), param.getOperator());
	                break;
				}
				case OPERATION_SUSPEND:{  //废弃
					activitiService.suspendProcessInstance(task.getProcessInstanceId());
					break;
				}
				case OPERATION_RECALL:{ //撤回
					HistoricProcessInstance historicProcessInstance = activitiService.getHistoricProcessInstance(task.getProcessInstanceId());
					if(!param.getOperator().equals(historicProcessInstance.getStartUserId())) {
						return MessageBean.create(BusinessCode.FAILURE.getCode(), "当前操作人员非流程起草人员", Void.class);
					}
					SubHistoryBillsMapper subMapper = factory.getMapper(SubHistoryBillsMapper.class);
					SubHistoryBillsBean subBill = subMapper.findByProcessInstanceId(task.getProcessInstanceId());
					if(subBill != null) {
						activitiService.suspendProcessInstance(task.getProcessInstanceId());//废弃子流程
						task = activitiService.getTaskById(subBill.getTaskId());
					}
					UserTask draftNode = activitiService.getDraftNode(task.getProcessDefinitionId());
					activitiService.backProcess(task, bill.getTitle(), param.getOperator(), draftNode.getId(), variables);
					break;
				}
				case OPERATION_REPLY:{ //回复沟通 添加沟通的信息
					if (StringUtils.isEmpty(param.getComment())) {
	                    return MessageBean.create(BusinessCode.FAILURE.getCode(), "请传入沟通、评论信息！", Void.class);
	                }
					CommunicatingBean bean = null;
					CommunicatingMapper mapper = factory.getMapper(CommunicatingMapper.class);
					List<CommunicatingBean> communicatings = mapper.findByTaskId(task.getId());
					for(CommunicatingBean communicating : communicatings) {
						if(Objects.equal(communicating.getTo(), param.getOperator())) {
							bean = communicating;
							break;
						}
					}
					if(bean == null) {
						 return MessageBean.create(BusinessCode.FAILURE.getCode(), "人员" + param.getOperator() + "未在当前节点沟通人列表，禁止添加沟通信息!", Void.class);
					}
					operatopn = operatopn + "(" + WorkflowUtil.getStaffByAccount(bean.getFrom()).getName() + ")";
					relationService.updateRelation(factory, 64, new RelationBean(RelationBean.R_REPLY, param.getOperator(), task.getProcessInstanceId(), task.getId(), RelationBean.STATUS_UNHANDLE), null);
					mapper.deleteByTaskIdAndTo(task.getId(), param.getOperator()); //删除沟通人
					break;
				}
				case OPERATION_CANCLE_COMMUNICATE:{ // 取消沟通人员
					if (StringUtils.isEmpty(param.getRelatedUser())) {
	                    return MessageBean.create(BusinessCode.FAILURE.getCode(), "缺少参数", Void.class);
	                }
					String relatedUsers = param.getRelatedUser();
					String[] users = relatedUsers.split(",");
					CommunicatingMapper mapper = factory.getMapper(CommunicatingMapper.class);
					for(String u : users) {
						StaffBean staff = WorkflowUtil.getStaffByCode(u);
						mapper.deleteByTaskIdAndTo(task.getId(), staff.getAccount());
						relationService.deleteRelation(factory, task.getId(), RelationBean.R_REPLY, staff.getAccount());
					}
					break;
				}
				case OPERATION_AGREE:{ //通过
	                if (StringUtils.isNotEmpty(param.getOtherNodeCandidates())) {
	                	//更新实例变量中  节点候选组信息
	                   activitiService.saveOtherNodeCandidates(param.getOtherNodeCandidates(), task.getExecutionId());
	                }
	                activitiService.complete(task.getId(), param.getOperator(), bill.getTitle(), variables);
	                SubHistoryBillsMapper mapper = factory.getMapper(SubHistoryBillsMapper.class);
					SubHistoryBillsBean subBill = mapper.findByProcessInstanceId(task.getProcessInstanceId());
					if(subBill != null && subBill.getStatus() == 2) {
						activitiService.complete(subBill.getTaskId(), param.getOperator(), bill.getTitle(), variables);
						processInstanceId = bill.getProcessInstanceId();
					}
	                break;
				}
				default:
					break;
			} 
	        tasks = activitiService.getTaskByProcessInsId(processInstanceId);
	        for(Task t : tasks) {
	        	String subFlowCode = activitiService.getSubTaskCode(t);
	        	// 假如该任务节点为子流程节点
	        	if(StringUtils.isNotBlank(subFlowCode)) {
	        		ProcessStartQueryBean query = new ProcessStartQueryBean();
	        	 	query.setFormVariables(param.getFormVariables());
	        		query.setSetCode(param.getSetCode());
	        		query.setProcessCode(param.getProcessCode());
	        		query.setOperator(param.getOperator());
	        		query.setBillId(param.getBillId());
	        		query.setTitle(bill.getTitle());
	        		list.add(startSubProcess(factory, query, t, subFlowCode));//启动子流程
	        	} else {
	        		TimerMapper timerMapper = factory.getMapper(TimerMapper.class);
	        		TimerBean timer = timerMapper.findByTaskIdAndStatus(t.getId(), 0);
	        		if(timer != null) {
	        			activitiService.complete(t.getId(), "system_operator", bill.getTitle(), variables);
	        			timerMapper.updateStatus(t.getId(), 1);
	        			tasks = activitiService.getTaskByProcessInsId(processInstanceId);
	        		}
	        	}
	        }
	        list.add(activitiService.getNextTaskNodeInfoDto(tasks));
	        TaskNodeInfoDto taskNodeInfo = new TaskNodeInfoDto();
	        taskNodeInfo.setPreNodeCode(activitiService.getTaskCode(task));
	        taskNodeInfo.setPreNodeName(task.getName());
	        taskNodeInfo.setProcessInstanceId(task.getProcessInstanceId());
	        List<NodeInfoDto> curNodes = new ArrayList<NodeInfoDto>();
	        List<TaskInfoDto> ts = new ArrayList<TaskInfoDto>();
	        for(NextTaskNodeInfoDto taskNode : list) {
	        	curNodes.addAll(taskNode.getCurNodes());
	        	ts.addAll(taskNode.getTasks());
	        }
	        taskNodeInfo.setCurNodes(curNodes);
	        taskNodeInfo.setCandidatesName(ts.stream().map(t -> t.getUserName()).collect(Collectors.joining(",")));
	        taskNodeInfo.setTasks(ts);
	        historyTaskMsgService.addHistoryTaskMsg(factory, param.getOperator(), task.getId(), operatopn, param.getComment(), param.getAttachment());
	        MessageBean<TaskNodeInfoDto> message = MessageBean.create(BusinessCode.SUCCESS.getCode(), "执行成功!", TaskNodeInfoDto.class);
	        message.setData(taskNodeInfo);
	        return message;
		}finally {
			SessionFactoryLocal.remove();
		}
	}
	
	/**
	 * 获取流程提交表单
	 * @param factory
	 * @param taskId
	 * @param processInstanceId
	 * @param operator
	 * @param processCode
	 * @return MessageBean
	 */
	@Override
	@TaskAnnotation("view")
	public MessageBean<?> view(SessionFactory factory, String taskId, String processInstanceId, String billId, String operator, String setCode, String processCode) {
		MessageBean<ModelAndView> message = MessageBean.create(BusinessCode.SUCCESS.getCode(), "查询成功", ModelAndView.class);
		HistoryBillsMapper mapper = factory.getMapper(HistoryBillsMapper.class);
		ProcessMapper processMapper = factory.getMapper(ProcessMapper.class);
		ProcessBean processBean = processMapper.queryByCodeAndSetCode(processCode, setCode);
		if(processBean == null) {
			message.setCode(BusinessCode.FAILURE.getCode());
			message.setDescription("未找到流程模板");
			return message;
		}
		ModelAndView model = null;
		if(StringUtils.isEmpty(processInstanceId)) {
			model = Tools.getView("form-create.btl");
			model.addObject("svg", processBean.getSvgContent());
			model.addObject("setCode", setCode);
			model.addObject("processCode", processCode);
			message.setData(model);
			return message;
		}
		HistoryBillsBean historyBill = mapper.findByProcessInstanceId(processInstanceId);
		if(historyBill == null) {
			message.setCode(BusinessCode.FAILURE.getCode());
			message.setDescription("该流程实例：" + processInstanceId + "未找到对应的单据信息");
			return message;
		}
		HistoryTaskMsgMapper historyTaskMsgMapper = factory.getMapper(HistoryTaskMsgMapper.class);
		SubHistoryBillsMapper subMapper = factory.getMapper(SubHistoryBillsMapper.class);
		List<HistoryTaskMsgBean> historyTaskMsgs = historyTaskMsgMapper.findByProcessInstanceId(processInstanceId);
		List<HistoryTaskMsgDto> dtoList = new ArrayList<HistoryTaskMsgDto>();
		List<HistoryTaskMsgBean> subHisTaskMsgs = new ArrayList<HistoryTaskMsgBean>();
		List<SubHistoryBillsBean> subBills = subMapper.findByParentProcessInstanceId(processInstanceId);
		subBills.stream().forEach(e -> subHisTaskMsgs.addAll(historyTaskMsgMapper.findByProcessInstanceId(e.getProcessInstanceId())));
		historyTaskMsgs.addAll(subHisTaskMsgs);
		historyTaskMsgs = historyTaskMsgs.stream().sorted(Comparator.comparing(HistoryTaskMsgBean::getOperationTime)).collect(Collectors.toList());
		CommunicatingMapper communicatingMapper = factory.getMapper(CommunicatingMapper.class);
		for(HistoryTaskMsgBean task : historyTaskMsgs) {
			HistoryTaskMsgDto dto = new HistoryTaskMsgDto();
			dto.setComment(task.getComment());
			dto.setNowNodeName(task.getNowNodeName());
			dto.setOperationTime(DateUtils.parseDate(task.getOperationTime(), "yyyy-MM-dd HH:mm:ss"));
			dto.setOperationUser(task.getOperationUser());
			dto.setOperationUserName(task.getOperationUserName());
			dto.setOperation(task.getOperation());
			dto.setNowNodeId(task.getNowNodeId());
			dtoList.add(dto);
		}
		Set<String> assigneAccounts = historyTaskMsgs.stream().map(t -> t.getOperationUser()).collect(Collectors.toSet());
		String assignes = assigneAccounts.stream().map(t -> {
			StaffBean staff = WorkflowUtil.getStaffByAccount(t);
			return staff.getName() + "(" + staff.getDepartmentname() + ")";
		}).collect(Collectors.joining(","));
		Task task = null;
		boolean draftOperate = false;
		SubHistoryBillsBean subBill = null;
		String candidateUserNames = null;
		if(StringUtils.isNotEmpty(taskId)) {
			List<String> candidateUsers = activitiService.getCandidateUsers(taskId);
			if(candidateUsers.contains(operator)) {
				task = activitiService.getTaskById(taskId);
			}
		}
		RelationQueryBean relationQuery = new RelationQueryBean();
		if(task == null && StringUtils.isNotEmpty(billId)) {
			List<SubHistoryBillsBean> subBills2 = subMapper.findByParentProInsIdAndStatus(processInstanceId, 1);
			if(subBills2 != null && subBills2.size() > 0) {
				subBill = subBills2.get(0);
				processInstanceId = subBill.getProcessInstanceId();
			}
			relationQuery.setProcessInstanceId(processInstanceId);
			relationQuery.setUserAccount(operator);
			relationQuery.setStatus(0);
			relationQuery.setRelation(2);
			List<RelationBean> relations = relationService.listRelation(factory, relationQuery);
			if(relations != null && relations.size() > 0) {
				RelationBean relation = relations.get(0);
				task = activitiService.getTaskById(relation.getTaskId());
			}
		} 
		List<Task> tasks = activitiService.getTaskByProcessInsId(processInstanceId);
		if(tasks != null && tasks.size() > 0) {
			candidateUserNames = tasks.stream().map(t -> {
				List<String> candidateUsers = activitiService.getCandidateUsers(t.getId());
				return candidateUsers.stream().map(e -> WorkflowUtil.getStaffByAccount(e).getName()).collect(Collectors.joining(","));
			}).collect(Collectors.joining(","));
			if(historyBill.getCreateUser().equals(operator)) {
				draftOperate = true;
			}
		}
		if(StringUtils.isNotEmpty(processInstanceId) && task == null) {
			model = Tools.getView("form-view.btl");
			model.addObject("svg", processBean.getSvgContent());
			model.addObject("assignees", assignes);
			model.addObject("setCode", setCode);
			model.addObject("taskId", draftOperate == true ? tasks.get(0).getId() : "");
			model.addObject("draftOperate", draftOperate);
			model.addObject("processCode", processCode);
			model.addObject("history", dtoList);
			if(tasks != null && tasks.size() > 0) {
				model.addObject("node", candidateUserNames);
			}else {
				model.addObject("node", "流程已结束");
			}
			message.setData(model);
			return message;
		}
		/**
		 * 审批(同意 驳回 转办 沟通) 待审批人员 可驳回的节点   1
		 * 回复 已添加沟通的人员 2
		 * 添加沟通 取消沟通 沟通发起人员 3
		 * 催办  4
		 */
		Collection<FlowElement> flowElements = activitiService.getFlowElements(task.getProcessDefinitionId());
		model = Tools.getView("form-approval.btl");
		model.addObject("svg", processBean.getSvgContent());
		model.addObject("assignees", assignes);
		model.addObject("setCode", setCode);
		model.addObject("processCode", processCode);
		model.addObject("history", dtoList);
		model.addObject("node", task.getName());
		model.addObject("operator", candidateUserNames);
		model.addObject("template", processBean.getCode());
		model.addObject("taskId", task.getId());
		model.addObject("suspended", task.isSuspended());
		model.addObject("param", activitiService.nodeParam(task, flowElements));
		model.addObject("processInstanceId", processInstanceId);
		model.addObject("activityId", tasks.stream().map(t -> t.getTaskDefinitionKey()).collect(Collectors.joining(",")));
		// 3  添加沟通  取消沟通
		List<CommunicatingBean> communicatings = communicatingMapper.findByTaskIdAndFrom(taskId, operator);
		if(communicatings != null && communicatings.size() > 0) {
			List<Map<String, String>> communicators = new ArrayList<Map<String, String>>();
			for(CommunicatingBean communicating : communicatings) {
				Map<String, String> map = new HashMap<String, String>();
				map.put("code", communicating.getTo());
				map.put("name", WorkflowUtil.getStaffByAccount(communicating.getTo()).getName());
				communicators.add(map);
			}
			model.addObject("viewType", "3");
			model.addObject("communicators", communicators);
			message.setData(model);
			return message; 
		}
		
		// 2 回复
		relationQuery.setTaskId(taskId);
		relationQuery.setUserAccount(operator);
		relationQuery.setProcessInstanceId(processInstanceId);
		relationQuery.setRelation(RelationBean.R_REPLY);
		relationQuery.setStatus(RelationBean.STATUS_UNHANDLE);
		List<RelationBean> relations = relationService.listRelation(factory, relationQuery);
		if(relations != null && relations.size() > 0) {
			model.addObject("viewType", "2");
			message.setData(model);
			return message; 
		}
		
		// 1 审批
		boolean isSub = false;
		relationQuery.setRelation(RelationBean.R_HANDLE);
		relations = relationService.listRelation(factory, relationQuery);
		if(relations != null && relations.size() > 0) {
			List<HistoricTaskInstance> hisTasks = new ArrayList<HistoricTaskInstance>();
			if(subBill != null && task.getTaskDefinitionKey().equals("Process-task0")) {
				isSub = true;
				Task pTask = activitiService.getTaskById(subBill.getTaskId());
				hisTasks = activitiService.getHistoricTaskByProcessInsId(subBill.getParentProcessInstanceId());
				hisTasks.stream().filter(t -> !t.getTaskDefinitionKey().equals(pTask.getTaskDefinitionKey()));
			} else {
				hisTasks = activitiService.getHistoricTaskByProcessInsId(processInstanceId);
			}
			hisTasks = hisTasks.stream().sorted(Comparator.comparing(HistoricTaskInstance::getStartTime)).collect(Collectors.toList());
			Map<String, String> reback = new LinkedHashMap<String, String>();
			for(HistoricTaskInstance hisTask : hisTasks) {
				reback.put(activitiService.getTaskCode(hisTask), hisTask.getName());
			}
			List<Map<String, String>> list = new ArrayList<Map<String, String>>();
			if(!activitiService.isDraftNode(task)) {
				Set<Entry<String, String>>  set = reback.entrySet();
				for(Entry<String, String> entry : set) {
					if(Objects.equal(entry.getKey() , task.getTaskDefinitionKey())) {
						break;
					}
					Map<String, String> data = new LinkedHashMap<String, String>();
					data.put("nowactivitiCode", isSub ? "1-" + entry.getKey() : entry.getKey());
					data.put("nowactivitiName", entry.getValue());
					list.add(data);
				}
			}
			model.addObject("viewType", "1");
			model.addObject("reback", list);
			model.addObject("selected", "");
			if(list.size() > 0) {
				Map<String, String> last = list.get(list.size() - 1);
				model.addObject("selected", last.get("nowactivitiCode"));
			}
			message.setData(model);
			return message; 
		}else {
			model.addObject("suspended", true);
			model.addObject("viewType", "1");
			message.setData(model);
			return message; 
		}
	}

	/**
	 * 获取该条单据下所有的历史节点的流转记录
	 * @param factory
	 * @param setCode 模板组编号
	 * @param processCode 模板编号
	 * @param billId 单据主键
	 * @return MessageBean 
	 */
	@Override
	@TaskAnnotation("history")
	public MessageBean<?> history(SessionFactory factory, String setCode, String processCode, String billId) {
		@SuppressWarnings("rawtypes")
		MessageBean<List> message = MessageBean.create(BusinessCode.SUCCESS.getCode(), "查询成功", List.class);
		HistoryBillsMapper historyBillMapper = factory.getMapper(HistoryBillsMapper.class);
		HistoryBillsBean historyBill = historyBillMapper.findByBillId(billId);
		if(historyBill == null) {
			message.setCode(BusinessCode.FAILURE.getCode());
			message.setDescription("未找到单据信息");
			return message;
		}
		HistoryTaskMsgMapper mapper = factory.getMapper(HistoryTaskMsgMapper.class);
		SubHistoryBillsMapper subMapper = factory.getMapper(SubHistoryBillsMapper.class);
		List<Task> tasks = activitiService.getTaskByProcessInsId(historyBill.getProcessInstanceId());
		List<HistoryTaskMsgBean> historyTaskMsgs = mapper.findByProcessInstanceId(historyBill.getProcessInstanceId());
		List<HistoryTaskMsgBean> subHisTaskMsgs = new ArrayList<HistoryTaskMsgBean>();
		List<SubHistoryBillsBean> subBills = subMapper.findByParentProcessInstanceId(historyBill.getProcessInstanceId());
		subBills.stream().forEach(e -> subHisTaskMsgs.addAll(mapper.findByProcessInstanceId(e.getProcessInstanceId())));
		historyTaskMsgs.addAll(subHisTaskMsgs);
		historyTaskMsgs = historyTaskMsgs.stream().sorted(Comparator.comparing(HistoryTaskMsgBean::getOperationTime)).collect(Collectors.toList());
		List<HistoryTaskMsgDto> dtoList = new ArrayList<HistoryTaskMsgDto>();
		try {
			for(int i = 0; i < historyTaskMsgs.size(); i++) {
				HistoryTaskMsgBean task = historyTaskMsgs.get(i);
				HistoryTaskMsgDto dto = new HistoryTaskMsgDto();
				BeanUtils.copyProperties(dto, task);
				dto.setOperationTime(DateUtils.parseDate(task.getOperationTime(), "yyyy-MM-dd HH:mm:ss"));
				if(i == historyTaskMsgs.size() - 1) {
					if(tasks != null && tasks.size() > 0) {
						List<TaskInfoDto> taskInfos = new ArrayList<TaskInfoDto>();
						dto.setTasks(taskInfos);
						for(Task t : tasks) {
							List<String> candidateUsers = activitiService.getCandidateUsers(t.getId());
							String userNames = candidateUsers.stream().map(s -> WorkflowUtil.getStaffByAccount(s).getName()).collect(Collectors.joining(","));
							String taskCode = activitiService.getTaskCode(t);
							taskInfos.add(new TaskInfoDto(StringUtils.join(candidateUsers, ","), userNames, t.getId(), taskCode, t.getName()));
						}
					}
				}else {
					dto.setTasks(new ArrayList<TaskInfoDto>());
				}
				dtoList.add(dto);
			}
			message.setData(dtoList);
		}catch(Exception ex) {
			log.error("出现未知异常：" + ex.getMessage());
			message.setCode(BusinessCode.FAILURE.getCode());
			message.setDescription("出现未知异常");
			return message;
		}
		return message;
	}

	@Override
	@TaskAnnotation("list")
	public MessageEntity<?> list(SessionFactory factory, ProcessListQueryBean param) {
		List<String> tasks = new ArrayList<String>();
		if(StringUtils.isNotEmpty(param.getAppCode())) {
			AppBean app = WorkflowUtil.getApp(param.getAppCode());
			if(app != null) {
				param.setAppId(app.getId());
			}
		}
		if(param.getType() == 3 || param.getType() == 4) {
			CommunicatingMapper cm = factory.getMapper(CommunicatingMapper.class);
			List<CommunicatingBean> coms = cm.findByFrom(param.getOperator());
			tasks = coms.stream().map(t -> t.getTaskId()).collect(Collectors.toList());
			param.setTaskId(tasks);
		}
		if(param.getType() == 4 && tasks.size() == 0) {
			return MessageEntity.success(new ArrayList<String>(), param.getPage(), 0, "查询成功");
		}
		Page<TaskListDto> page = PageHelper.startPage(param.getPage(), param.getPageCount());
		TaskListMapper mapper = factory.getMapper(TaskListMapper.class);
		List<TaskListDto> list = mapper.findTaskList(param);
		list.stream().forEach(t -> {
			if(t.getStatus() == 1) {
				List<Task> currentTasks = activitiService.getTaskByProcessInsId(t.getProcessInstanceId());
				List<String[]> users = currentTasks.parallelStream().map(k -> {
					String[] array = new String[2];
					List<String> u = activitiService.getCandidateUsers(k.getId());
					List<StaffBean> staff = WorkflowUtil.getStaffByAccount(u);
					array[0] = u.stream().collect(Collectors.joining(","));
					array[1] = staff.stream().filter(s -> s != null).map(s -> s.getName()).collect(Collectors.joining(","));
					return array;
				}).collect(Collectors.toList());
				String account = users.stream().map(u -> u[0]).collect(Collectors.joining(","));
				String name = users.stream().map(u -> u[1]).collect(Collectors.joining(","));
				t.setCurNodeUsers(account);
				t.setCurNodeUsersName(name);
			} else {
				t.setCurNodeUsers("");
				t.setCurNodeUsersName("");
			}
			t.setAppCode(param.getAppCode());
			t.setAppName(WorkflowUtil.getApp(param.getAppCode()).getName());
		});
        return MessageEntity.success(list, page.getPageNum(), page.getTotal(), "查询成功");
	}

	@Override
	@TaskAnnotation("define")
	public MessageBean<?> define(SessionFactory factory, String setCode, String processCode) {
		@SuppressWarnings("rawtypes")
		MessageBean<List> message = MessageBean.create(BusinessCode.SUCCESS.getCode(), "查询成功", List.class);
		message.setData(activitiService.workflowDefine(setCode + "_" + processCode));
		return message;
	}
	
	@Override
	@TaskAnnotation("deleteProcess")
	public MessageBean<?> deleteProcess(String processInstanceId) {
		ProcessInstance processInstance = activitiService.getProcessInstance(processInstanceId);
		if(processInstance == null) {
			return MessageBean.create(BusinessCode.FAILURE.getCode(), "未找到该流程", Void.class);
		}
		activitiService.deleteProcessInstance(processInstanceId);
		return MessageBean.create(BusinessCode.SUCCESS.getCode(), "删除成功", Void.class);
	}
	
	/**
	 * 流程挂起
	 */
	@Override
	public MessageBean<?> suspend(String processInstanceId) {
		ProcessInstance processInstance = activitiService.getProcessInstance(processInstanceId);
		if(processInstance == null) {
			return MessageBean.create(BusinessCode.FAILURE.getCode(), "未找到该流程", Void.class);
		}
		if(!processInstance.isSuspended()) {
			activitiService.suspendProcessInstance(processInstanceId);
			log.info("流程已挂起：{}", processInstanceId);
		}
		return MessageBean.create(BusinessCode.SUCCESS.getCode(), "执行成功", Void.class);
	}

	/**
	 * 将已挂起的流程恢复到执行状态
	 */
	@Override
	public MessageBean<?> recovery(String processInstanceId) {
		ProcessInstance processInstance = activitiService.getProcessInstance(processInstanceId);
		if(processInstance == null) {
			return MessageBean.create(BusinessCode.FAILURE.getCode(), "未找到该流程", Void.class);
		}
		if(processInstance.isSuspended()) {
			activitiService.activateProcessInstance(processInstanceId);
			log.info("流程已恢复：{}", processInstanceId);
		}
		return MessageBean.create(BusinessCode.SUCCESS.getCode(), "执行成功", Void.class);
	}
	
	private NextTaskNodeInfoDto startSubProcess(SessionFactory factory, ProcessStartQueryBean param, Task task, String subFlowCode) {
		Map<String, Object> vars = param.getFormVariables();
		ProcessDefinition pd = activitiService.getLastProcessDefinition(subFlowCode);
		if(pd == null) {
			throw new RuntimeException("该流程模板未发布");
		}
		UserTask userTask = activitiService.getDraftNode(pd.getId());
		if(userTask != null && userTask.getCandidateUsers().size() > 0) {
			List<String> draftNodeUsers = userTask.getCandidateUsers();
			Map<String, List<ExtensionAttribute>> operationmap = userTask.getExtensionElements().get("operation").get(0).getAttributes();
			String filterRule = operationmap.get("filter_rule").get(0).getValue(); //获取过滤规则
			String autoPickRule = operationmap.get("auto_pick_rule").get(0).getValue(); //获取自动选人规则
			draftNodeUsers = ApiUtils.convertUsers(draftNodeUsers, param.getOperator(), filterRule, autoPickRule, vars);
			if(!draftNodeUsers.contains(param.getOperator())) {
				throw new RuntimeException("当前人员未在起草节点候选人中，禁止开启流程实例!");
			}
		}
		ProcessInstance processInstance = activitiService.startProcessInstanceByKey(subFlowCode, param.getOperator(), param.getTitle(), param.getBillId(), vars);
		List<Task> tasks = activitiService.getTaskByProcessInsId(processInstance.getId());
        if (null == tasks || tasks.size() == 0) {
            throw new RuntimeException("实例启动失败，获取当前任务失败!");
        }
        if(tasks.size() == 1 && activitiService.isDraftNode(tasks.get(0))) {
            // 起草节点自动办理
            activitiService.complete(tasks.get(0).getId(), param.getOperator(), param.getTitle(), vars);
            tasks = activitiService.getTaskByProcessInsId(processInstance.getId());
        }
        SubHistoryBillsMapper mapper = factory.getMapper(SubHistoryBillsMapper.class);
        SubHistoryBillsBean bean = new SubHistoryBillsBean();
        bean.setBillId(param.getBillId());
        bean.setId(WorkflowUtil.getObjectId());
        bean.setParentProcessInstanceId(task.getProcessInstanceId());
        bean.setProcessInstanceId(processInstance.getId());
        bean.setTaskId(task.getId());
        bean.setStatus(1);
        mapper.addSubHistoryBills(bean);
        // 获取下一个任务节点的信息
		return activitiService.getNextTaskNodeInfoDto(tasks); 
	}
	
	private void alram(Task task, String content, String operator) {
		int noticeway = -1;
        Collection<FlowElement> flowElements = activitiService.getFlowElements(task.getProcessDefinitionId());
        for(FlowElement element : flowElements) {
    		if(element.getId().equals(task.getTaskDefinitionKey())) {
    			String value = element.getExtensionElements().get("operation").get(0).getAttributes().get("noticeway").get(0).getValue();
    			if(StringUtils.isEmpty(value) || Integer.parseInt(value) == 0) {
    				noticeway = 2;
    			}
    			List<String> users = activitiService.getCandidateUsers(task.getId());
    			List<StaffBean> staffs = WorkflowUtil.getStaffByAccount(users);
    			for(StaffBean staff : staffs) {
    	    		//邮件
    	    		if((noticeway & 2) == 2 && StringUtils.isNotEmpty(staff.getEmail())) {
    	    			WorkflowUtil.sendMail(staff.getEmail(), content);
    	    			log.info("邮件发送催办成功 发送人员：{} 接收人员：{}", operator, staff.getName());
    	    		}
    	    		//短信
    	    		if((noticeway & 4) == 4 && StringUtils.isNotEmpty(staff.getMobile())) {
    	    			WorkflowUtil.sendMessage(staff.getMobile(), content);
    	    			log.info("短信发送催办成功 发送人员：{} 接收人员：{}", operator, staff.getName());
    	    		}
    			}
    		}
    	}
	}
}
